﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Linq;
using PB.Base;
using System.Xml.Serialization;
using PB.Helpers.CustomCollections;
using PB.Enums;
using System.Collections.ObjectModel;

namespace PB.Helpers
{
    internal static class XmlHelper
    {
        private static XDocument _docModel = new XDocument(
                new XDeclaration("1.0", "UTF-8", "yes"),
                new XElement("Logs",
                    new XElement("Errors"),
                    new XElement("Warnings"),
                    new XElement("Informations"),
                    new XElement("Events"),
                    new XElement("Users"))
                    );

        private static string _path = String.Empty;

        public static void SetPath(string path)
        {
            try
            {
                _path = path;

                if (!File.Exists(path))
                {
                    Create(path);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private static void Create(string path)
        {
            try
            {
                _docModel.Save(path);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public static void Create()
        {
            try
            {
                _docModel.Save(_path);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public static void Delete(string path)
        {
            try
            {
                if (!File.Exists(path))
                {
                    throw new FileNotFoundException();
                }
                else
                {
                    File.Delete(path);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public static void Delete()
        {
            try
            {
                if (!File.Exists(_path))
                {
                    throw new FileNotFoundException();
                }
                else
                {
                    File.Delete(_path);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public static class Logs
        {
            private static void CheckLog(Log log, bool logType, bool message, bool dateTime)
            {
                if (logType && log.LogType == LogType.None)
                    throw new ArgumentNullException("'LogType' is a required value.");

                else if (message && String.IsNullOrWhiteSpace(log.Message))
                    throw new ArgumentNullException("'Message' is a required value.");

                else if (dateTime && log.DateTime.IsNull())
                    throw new ArgumentNullException("'DateTime' is a required value.");
            }

            public static void WriteLog(Log log)
            {
                CheckLog(log, true, true, false);
         
                try
                {                    
                    XDocument xdoc = XDocument.Load(_path);

                    Utilities.EnumLogs enumLogs = new Utilities.EnumLogs(log.LogType);

                    XElement xelem = new XElement(enumLogs.NameElement,
                    new XAttribute("Message", log.Message),
                    new XAttribute("DateTime", log.DateTime.ToShortDateString() + "-"
                                            + log.DateTime.ToLongTimeString()));

                    if (log.User != null && !String.IsNullOrWhiteSpace(log.User.Username))
                    {
                        XAttribute usernameAttr = new XAttribute("User", log.User.Username);

                        xelem.Add(usernameAttr);
                    }

                    xdoc.Element("Logs").Element(enumLogs.NameElements).Add(xelem);

                    xdoc.Save(_path);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }

            public static void DeleteLog(Log log, bool userOwnerLog)
            {
                if (!userOwnerLog)
                {
                    CheckLog(log, true, false, false);                    
                }
                else
                {
                    if (log.User == null) throw new ArgumentNullException("'User' is required if the user is owner of the log.");
                }

                try
                {
                    XDocument xdoc = XDocument.Load(_path);

                    Utilities.EnumLogs enumLogs = new Utilities.EnumLogs(log.LogType);

                    var query = xdoc.Element("Logs").Element(enumLogs.NameElements).Elements(enumLogs.NameElement);

                    if (!log.DateTime.IsNull())
                    {
                        query = query.Where(q => q.Attribute("DateTime").Value == log.DateTime.ToShortDateString() + "-" +
                            log.DateTime.ToLongTimeString());
                    }
                    if (!String.IsNullOrWhiteSpace(log.Message))
                    {
                        query = query.Where(q => q.Attribute("Message").Value == log.Message);
                    }
                    if (log.User != null && !String.IsNullOrWhiteSpace(log.User.Username))
                    {
                        query = query.Where(q => q.Attribute("User") != null && q.Attribute("User").Value == log.User.Username);
                    }

                    if (userOwnerLog)
                    {                                                
                        query.Remove();
                    }
                    else
                    {
                        foreach (var q in query)
                        {
                            q.Attribute("User").Remove();
                        }
                    }
                    
                    #region EVENTUALE
                    
                    //if (log.DateTime.IsNull() && log.LogType !=null && !String.IsNullOrWhiteSpace(log.Message)
                    //    && log.User != null && !String.IsNullOrWhiteSpace(log.User.Username))
                    //{
                    //    query = query.Where(q => q.Attribute("Message").Value == log.Message &&
                    //        q.Attribute("Username").Value == log.User.Username
                    //        ).Cast<XElement>();
                    //}
                    //else if (log.DateTime.IsNull() && log.LogType != null && String.IsNullOrWhiteSpace(log.Message)
                    //    && log.User != null && !String.IsNullOrWhiteSpace(log.User.Username))
                    //{
                    //    query = query.Where(q => q.Attribute("Username").Value == log.User.Username
                    //            ).Cast<XElement>();
                    //}
                    //else if (log.DateTime.IsNull() && log.LogType != null && !String.IsNullOrWhiteSpace(log.Message)
                    //    && String.IsNullOrWhiteSpace(log.User.Username))
                    //{
                    //    query = query.Where(q => q.Attribute("Message").Value == log.Message
                    //            ).Cast<XElement>();
                    //}
                    //else if (!log.DateTime.IsNull() && log.LogType != null && !String.IsNullOrWhiteSpace(log.Message)
                    //    && String.IsNullOrWhiteSpace(log.User.Username))
                    //{

                    //}

                    #endregion                    

                    xdoc.Save(_path);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }

            public static void DeleteAllLogs()
            {
                try
                {
                    XDocument xdoc = XDocument.Load(_path);

                    var el = xdoc.Element("Logs");

                    el.Element("Errors").RemoveAll();
                    el.Element("Informations").RemoveAll();
                    el.Element("Events").RemoveAll();
                    el.Element("Warnings").RemoveAll();

                    xdoc.Save(_path);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }

            public static bool ContainsLog(Log log)
            {
                try
                {                    
                    IEnumerable<Log> query = GetLogs();

                    if (!log.DateTime.IsNull())
                    {
                        query = query.Where(q => q.DateTime == log.DateTime);
                    }
                    if (!String.IsNullOrWhiteSpace(log.Message))
                    {
                        query = query.Where(q => q.Message == log.Message);
                    }
                    if (log.User != null && !String.IsNullOrWhiteSpace(log.User.Username))
                    {
                        query = query.Where(q => q.User != null && q.User.Username == log.User.Username);
                    }

                    if (query.Count() > 0) return true;

                    return false;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }

            public static List<Log> GetLogs()
            {
                XDocument xdoc = XDocument.Load(_path);

                var result = new List<Log>();

                var errors = xdoc.Element("Logs").Element("Errors").Elements("Error");
                var events = xdoc.Element("Logs").Element("Events").Elements("Event");
                var info = xdoc.Element("Logs").Element("Informations").Elements("Info");
                var warnings = xdoc.Element("Logs").Element("Warnings").Elements("Warning");

                if (errors.Count() > 0)
                {
                    foreach (var er in errors)
                    {
                        Log logErr = new Log()
                        {
                            DateTime = Utilities.GetXmlDateTime(er.Attribute("DateTime").Value),
                            Message = er.Attribute("Message").Value,
                            LogType = LogType.Error,
                            User = Utilities.GetUserLog(er)
                        };

                        result.Add(logErr);
                    }
                }
                
                if (events.Count() > 0)
                {
                    foreach (var ev in events)
                    {
                        Log logEv = new Log()
                        {
                            DateTime = Utilities.GetXmlDateTime(ev.Attribute("DateTime").Value),
                            Message = ev.Attribute("Message").Value,
                            LogType = LogType.Event,
                            User = Utilities.GetUserLog(ev)
                        };

                        result.Add(logEv);
                    }
                }
                
                if (info.Count() > 0)
                {
                    foreach (var i in info)
                    {
                        Log logInf = new Log()
                        {
                            DateTime = Utilities.GetXmlDateTime(i.Attribute("DateTime").Value),
                            Message = i.Attribute("Message").Value,
                            LogType = LogType.Info,
                            User = Utilities.GetUserLog(i)
                        };

                        result.Add(logInf);
                    }
                }
               
                if (warnings.Count() > 0)
                {
                    foreach (var w in warnings)
                    {
                        Log logWa = new Log()
                        {
                            DateTime = Utilities.GetXmlDateTime(w.Attribute("DateTime").Value),
                            Message = w.Attribute("Message").Value,
                            LogType = LogType.Warning,
                            User = Utilities.GetUserLog(w)
                        };

                        result.Add(logWa);
                    }
                }

                return result;
            }
        }

        public static class Users
        {
            public static void WriteUser(User user)
            {
                try
                {                    
                    XDocument xdoc = XDocument.Load(_path);

                    xdoc.Element("Logs").Element("Users").Add(
                        new XElement("User",
                            new XAttribute("Username", user.Username),
                            new XAttribute("Rule", user.Rule),
                            new XAttribute("CreatedDateTime", user.CreatedDateTime.ToShortDateString() + "-"
                                            + user.CreatedDateTime.ToLongTimeString())
                            )
                            );

                    xdoc.Save(_path);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }

            public static void DeleteUser(User user)
            {               
                try
                {
                    XDocument xdoc = XDocument.Load(_path);

                    var query = from xml in xdoc.Element("Logs").Element("Users").Elements("User")
                                where xml.Attribute("Username").Value == user.Username
                                select xml;

                    query.Remove();

                    xdoc.Save(_path);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }

            public static void DeleteAllUsers()
            {
                try
                {
                    XDocument xdoc = XDocument.Load(_path);

                    xdoc.Element("Logs").Element("Users").RemoveAll();

                    xdoc.Save(_path);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }

            public static bool ContainsUser(User item)
            {
                bool contains = false;

                var users = GetUsers().AsEnumerable();

                bool ruleEmpty = String.IsNullOrWhiteSpace(item.Rule);

                if (!item.CreatedDateTime.IsNull())
                {
                    users = users.Where(
                        u => u.CreatedDateTime == item.CreatedDateTime
                        );
                }
                if (!ruleEmpty)
                {
                    users = users.Where(
                        u => u.Rule == item.Rule
                        );
                }
                if (!String.IsNullOrWhiteSpace(item.Username))
                {
                    users = users.Where(
                        u => u.Username == item.Username);
                }

                if (users.Count() > 0)
                    contains = true;
                else
                    contains = false;

                return contains;
            }

            public static List<User> GetUsers()
            {
                try
                {
                    XDocument xdoc = XDocument.Load(_path);

                    List<User> result = new List<User>();
                    
                    var users = xdoc.Element("Logs").Element("Users").Elements("User");

                    if (users != null && users.Count() > 0)
                    {
                        foreach (var u in users)
                        {
                            User user = new User()
                            {
                                Username = u.Attribute("Username").Value,
                                Rule = u.Attribute("Rule").Value,
                                CreatedDateTime = Utilities.GetXmlDateTime(u.Attribute("CreatedDateTime").Value)
                            };
                            
                            result.Add(user);
                        }
                    }

                    return result;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
        }
    }
}
